// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "zbi.h"

#include <libzbi/zbi-zx.h>
#include <stdio.h>
#include <zircon/status.h>

zx_status_t netboot_prepare_zbi(zx_handle_t nbkernel_vmo, zx_handle_t nbbootdata_vmo,
                                const uint8_t* cmdline, uint32_t cmdline_size,
                                zx_handle_t* kernel_vmo, zx_handle_t* bootdata_vmo) {
  zbi::ZbiVMO kernel, data;

  if (nbkernel_vmo == ZX_HANDLE_INVALID) {
    printf("netbootloader: no kernel!\n");
    return ZX_ERR_INVALID_ARGS;
  }

  if (nbbootdata_vmo == ZX_HANDLE_INVALID) {
    // Split the complete ZBI into its kernel and data parts.
    zbi::ZbiVMO zbi;
    auto status = zbi.Init(zx::vmo{nbkernel_vmo});
    if (status != ZX_OK) {
      printf("netbootloader: can't map complete ZBI: %d (%s)\n", status,
             zx_status_get_string(status));
      return status;
    }
    auto result = zbi.SplitComplete(&kernel, &data);
    if (result != ZBI_RESULT_OK) {
      printf("netbootloader: invalid complete ZBI: %d\n", result);
      return ZX_ERR_INTERNAL;
    }
  } else {
    // Old-style boot with separate kernel and data ZBIs.
    printf(
        "netbootloader: old-style boot is deprecated;"
        " switch to complete ZBI!\n");
    auto status = kernel.Init(zx::vmo{nbkernel_vmo});
    if (status != ZX_OK) {
      printf("netbootloader: can't map kernel ZBI: %d (%s)\n", status,
             zx_status_get_string(status));
      return status;
    }
    status = data.Init(zx::vmo{nbbootdata_vmo});
    if (status != ZX_OK) {
      printf("netbootloader: can't map kernel ZBI: %d (%s)\n", status,
             zx_status_get_string(status));
      return status;
    }
  }

  if (cmdline_size > 0) {
    auto result = data.AppendSection(cmdline_size, ZBI_TYPE_CMDLINE, 0, 0, cmdline);
    if (result != ZBI_RESULT_OK) {
      printf("netbootloader: failed to append command line: %d\n", result);
      return ZX_ERR_INTERNAL;
    }
  }

  printf("netbootloader: kernel ZBI %#x bytes data ZBI %#x bytes\n", kernel.Length(),
         data.Length());

  *kernel_vmo = kernel.Release().release();
  *bootdata_vmo = data.Release().release();
  return ZX_OK;
}
